/* 
 * File:   keyinput.h
 * Author: lishang
 *
 * Created on 2018年6月14日, 上午10:57
 */

#ifndef KEYINPUT_H
#define	KEYINPUT_H
#define MAX_HISTORY_SIZE 100
#include <termios.h>  
#include <stdio.h>    
#include <unistd.h>
#include <fcntl.h>  
#include   <sys/ioctl.h>   
#include   <linux/kd.h>   
#include <assert.h>  
#include <string.h>  
#include <vector>
#include <map>
#include <linux/input.h>
#include <fcntl.h>

#include <signal.h>
#include <unistd.h> 

//#define mCursor( len ,rmToEnd)  cursorMove(len,rmToEnd) ; i+=len
//#define nChar(...) 1
//#define nShow(s) (int)strlen(s)

#ifndef mCursor
//中文支持 mCursor
char m_cmdStr[500];
int m_c_len,m_len,m_num;
bool m_dir;

#define  mCursor( len ,rmToEnd)                           \
    m_dir=(len)>0;                                                            \
    strcpy(m_cmdStr,m_dir ? in2.c_str() : in);           \
    m_len=len;                                                                   \
    j=m_dir? 0:i-1;                                                             \
    m_num=abs(m_len);                                                \
    while(m_num--){                                                        \
        m_c_len=nChar(&m_cmdStr[j],m_dir);            \
        j+=m_dir?m_c_len:-m_c_len;                               \
        if(abs(m_c_len)>1)  m_len+=m_dir?1:-1;         \
    }                                                                                      \
    i+=m_dir ?j :j-(i-1);                                                     \
    cursorMove(m_len,rmToEnd);

#define nChar(...) strlenChar(__VA_ARGS__)
#define nShow(s) (int)strlenShow(s)

#endif

string cmd;
vector <string> historyCmd;
int kbhit(void);
int getch(char *s);
int getch();
void getInput(char *in);
long stoi(char *s);

struct termios org_termios_opts;

/**
 * 异常退出
 */
static void input_abort(int x)
{
    if (tcsetattr(STDIN_FILENO, TCSANOW, &org_termios_opts) == -1)
        perror("tcsetattr");
 
    exit(1);
}

void input_init(){
    tcgetattr(STDIN_FILENO, &org_termios_opts);

// the program terminates when there is no input for 10 secs
    signal(SIGALRM, input_abort);
 
    // if we receive a signal, we want to exit nicely, in
    // order not to leave the keyboard in an unusable mode
    signal(SIGHUP, input_abort);
    signal(SIGINT, input_abort);
    signal(SIGQUIT, input_abort);
    signal(SIGILL, input_abort);
    signal(SIGTRAP, input_abort);
    signal(SIGABRT, input_abort);
    signal(SIGIOT, input_abort);
    signal(SIGFPE, input_abort);
    signal(SIGKILL, input_abort);
    signal(SIGUSR1, input_abort);
    signal(SIGSEGV, input_abort);
    signal(SIGUSR2, input_abort);
    signal(SIGPIPE, input_abort);
    signal(SIGTERM, input_abort);
#ifdef SIGSTKFLT
    signal(SIGSTKFLT, input_abort);
#endif
    signal(SIGCHLD, input_abort);
    signal(SIGCONT, input_abort);
    signal(SIGSTOP, input_abort);
    signal(SIGTSTP, input_abort);
    signal(SIGTTIN, input_abort);
    signal(SIGTTOU, input_abort);
}

int kbhit(void) {
    struct termios oldt, newt;
    int ch;
    int oldf;
    tcgetattr(STDIN_FILENO, &oldt);
    newt = oldt;
    newt.c_lflag &= ~(ICANON | ECHO);
    tcsetattr(STDIN_FILENO, TCSANOW, &newt);
    oldf = fcntl(STDIN_FILENO, F_GETFL, 0);
    fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK);
    ch = getchar();
    tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
    fcntl(STDIN_FILENO, F_SETFL, oldf);
    if (ch != EOF) {
        ungetc(ch, stdin);
        return 1;
    }
    return 0;
}  

   /**
    * 从网上摘抄的 函数 系统默认的getchar函数需要输入回车换行  
    */
    char _getch()  
    { 
       char c=0;  
       struct termios org_opts, new_opts;  
       int res=0;  
       //-----  store old settings -----------  
       res=tcgetattr(STDIN_FILENO, &org_opts);  
       assert(res==0);  
       //---- set new terminal parms --------  
       memcpy(&new_opts, &org_opts, sizeof(new_opts));  
       new_opts.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHOK | ECHONL | ECHOPRT | ECHOKE | ICRNL);  
       tcsetattr(STDIN_FILENO, TCSANOW, &new_opts);  
       c=getchar();  
       //------  restore old settings ---------  
       res=tcsetattr(STDIN_FILENO, TCSANOW, &org_opts);  
       assert(res==0);  
       return c;  
    }
 
  int getch(char *s){
        char c=_getch();
        int i=1;
        s[0]=c;
        if(kbhit()){
            while(kbhit()){
                s[i++]=_getch();
            }
        }
        s[i]=0;
        
        return c;
    }
  
  int getch(){
      char s[10];
      return getch(s);
  }
  
int getBreakCode(){
        
}
  
/**
 * 字符串到整形数字显示 解决 switch只能解析整形数字的问题 
 */
long stoi(char *s){
    long rt=0;
    for(int i=0;i<strlen(s);i++) {
        rt=(s[i]<100)? rt*100+s[i] : rt*1000+s[i];
    }

    return rt;
}

 //中文字符截取
 void unshift(string &a,char *b,int len=1){
     int start =0;
     if(len<0)    {
         start=strlen(b)-1+len;
         len=strlen(b)-1;
     }
     for(int i=len;i>start;i--) {
            a=b[i]+a;
     }
 }

void cursorMove(int  len,bool rmToEnd=false){
    char dir;
    if(len<0){
        dir='D';
        len=-len;
    }else{
        dir='C';
    }
    
    if(len) printf("\033[%d%c",len,dir);
    if(rmToEnd) printf("\033[K");
}
        /**
         * 单字符获取 
         */
 void getInput(char *in){
            char c; 
            int i=strlen(in),j;
            static   int  hIndex;
            static char histroyChar;
            char s[500];
            bool insertFlag=true;
            string in2="";
            string editingCmd="";
            
            while(c=getch(s)){
                if(c==10) {
                    while(in2.length()) {
                        in[i++]=in2.at(0) ; 
                        in2.erase(0,1);
                    }
                    
                    printf("\n");
                    break;
                }
                else if(c==9){ //tab
                    c= KEY_TAB+128;
                    in2=in+in2;
                    for(j=0;j<i;j++) in[j+2]=in2.at(j);
                    in[j+2]=0;
                    i=0;
                    in[i++]=c;
                    break;
                }else if(c==127){  //退格
                    if(i){
                        mCursor(-1,true);
                        in[i]=0;
                        if(in2.size()>0) {
                            cout<<in2;
                            cursorMove(-nShow(in2.c_str()));
                        }
                        
//                        printf("\033[%dD\033[K",1);
//                        i--;
//                        in[i]=0;
                    }
                    continue;
                }else if(c>127){  //汉字 其他需要直接显示的字符  
                        for(j=0;j<strlen(s);j++)  
                            in[i++]=c;
                        printf("%s,",s);
                     continue;
                }else if(c==27 ){  //其他控制字符
                    bool quitFlag=false;
                    int hSize;
//                    int len=-1;bool rmToEnd=false;
                    switch(stoi(s)){
                        case 279168:  // <--
                              if(!i) break;                                          
                              mCursor(-1,false);
                              j=nChar(in,false);
                              unshift(in2,in,-j);
                              in[i]=0;
//                              printf("\033[%dD",1);
//                              in2=in[strlen(in)-1]+in2;
//                              in[strlen(in)-1]='\0';
//                              i--;
                            break;
                        case 279167:  // -->
                            if(in2.size()) {
                                    mCursor(1,false);  
                                    j=nChar(in2.c_str());
                                    strncat(in,in2.c_str(),j);
                                    while(j--) in2.erase(0,1);
//                                in[i++]=in2.at(0);
//                                in2.erase(0,1);
//                                printf("\033[%dC",1);
                            }
                            break;
                        case 279165:  //22 up
                            c=22;
                        case 279166:  //34 down
                            if(c==27)  c=34;
                            hSize=historyCmd.size();
                            if(histroyChar != 22 && histroyChar!=34)    {
                                hIndex=hSize;
                                if(c==34) break;
                                if(i && editingCmd.size()==0){
                                    in[i]=0; 
                                    editingCmd=in+in2;
                                }
                            }
                            
                            hIndex+= (c==22)? -1:1;
                            if(hIndex<0) {
                                hIndex=0;
                                break;
                            }
                            else if(hIndex>hSize-1) {
                                hIndex=hSize-1;
                                if(i) cursorMove(-nShow(in),true);//printf("\033[%dD\033[K",i);
                                i=0;
                                if(editingCmd.length()>0) {
                                    cout << editingCmd; 
                                    i=editingCmd.length();
                                }
                            }else{
                                if(i) cursorMove(-nShow(in),true); //printf("\033[%dD\033[K",i);
                                strcpy(in,historyCmd.at(hIndex).c_str());
                                printf("%s",in);
                                i=strlen(in);
                            }
                            in2="";
                            break;
                        case 277972:  //home
                                if(i){ 
                                    cursorMove(-nShow(in));
//                                    printf("\033[%dD",nShow(i));
                                    in2=in+in2;
                                    in[0]=i=0;
                                }
                                break;
                        case 277970:  //end
                            if(in2.length()>0) {
                                    cursorMove(nShow(in2.c_str()));
//                                    printf("\033[%ldC",in2.length());
                                    in = strcat(in,in2.c_str());
                                    in2.erase(0);
                                    i=strlen(in);
                            }
                            break;
                        case 279151126:  //del
                            if(in2.length()){
                                j=nChar(in2.c_str());
                               in2.erase(0,j);
                               mCursor(0,true);
                               printf("%s",in2.c_str());
                               cursorMove(-nShow(in2.c_str()));
//                                in2.erase(0,1);
//                                printf("\033[K%s",in2.c_str());
//                                printf("\033[%ldD",in2.length());
                            }
                            break;
                         case 279150126:   //insert
                             insertFlag=!insertFlag;
                             break;
                        case 279153126:  //pageup
                                if(c==27) c=KEY_PAGEUP;      
                        case 279154126:  //pagedown
                                if(c==27) c=KEY_PAGEDOWN;
                                
                        case 27919165:  //F1
                                if(c==27) c=KEY_F1; 
                        case 27919166:  
                                if(c==27) c=KEY_F2;
                        case 27919167:  
                                if(c==27) c=KEY_F3;
                        case 27919168:  
                                if(c==27) c=KEY_F4;
                        case 27919169:  
                                if(c==27) c=KEY_F5;
                                
                        case 274955126:  
                                if(c==27) c=KEY_F6;        
                        case 274956126:  
                                if(c==27) c=KEY_F7;
                        case 274957126:  
                                if(c==27) c=KEY_F8;
                        case 274958126:  
                                if(c==27) c=KEY_F9;
                                
                        case 275049126:  
                                if(c==27) c=KEY_F10;        
                        case 275051126:  
                                if(c==27) c=KEY_F11;
                        case 275052126:  
                                if(c==27) c=KEY_F12;
                        case 27:
                            if(c==27) c=KEY_ESC;
                        default:
                            c+=128;                            
                            quitFlag=true; 
                            in[i]=0;
                            in2=in+in2;
                            for(j=0;j<i;j++) in[j+2]=in2.at(j);
                            if(i==0)    { 
                                   in[2]=0;
                                   for(j=0;j<strlen(s);j++) in[j+2]=s[j];
                            }
                            i=0;
                            in[i++]=c;
                            break;
                    }
                    histroyChar=c;     
                    if(quitFlag)  break; else  continue;
                 }else if(c<32){//处理控制字符  比如 ctrl+T == 20
                     in2=in+in2;
                     for(j=0;j<i;j++) in[j+2]=in2.at(j);
                     i=0;
                     in[i++]=c;
                     in[i]=0;
                     return;
                 }
                
                histroyChar=c;
                
                if(s[1]) {   //粘贴的数据 
                    char *p=&s[0];
                    while(c=*p++){
                        in[i++]=c;
                    }
                    in[i]=0;
                    printf("%s",s);
                }else{
                    if(isprint(c))  printf("%c",c);
                    in[i++]=c;
                    in[i]=0;
                }
                
                 if(in2.length()>0) {
                     if(insertFlag) {
                         printf("%s",in2.c_str());
                         cursorMove(-nShow(in2.c_str()));
//                         printf("\033[%ldD",in2.length());
                     }else in2.erase(0,1);
                 }
            }
            histroyChar=c;
            in[i]='\0';
            
            if(i>0 && cmd.compare(in)!=0 && !(i==1 && in[0]<0)){
                cmd=in;
                historyCmd.push_back(cmd);
            }
             if(historyCmd.size() >MAX_HISTORY_SIZE )       historyCmd.erase(historyCmd.begin());
}


        
#endif	/* KEYINPUT_H */

